﻿using System;
using System.Linq;
using System.Linq.Expressions;

namespace Fuse8.DomainFramework.Common
{
    /// <summary>
    /// 
    /// </summary>
    public static class PredicateBuilder
    {
        /// <summary>
        /// 
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="expr1"></param>
        /// <param name="expr2"></param>
        /// <returns></returns>
        public static Expression<Func<T, bool>> And<T>(this Expression<Func<T, bool>> expr1, Expression<Func<T, bool>> expr2)
        {
            InvocationExpression right = Expression.Invoke(expr2, expr1.Parameters.Cast<Expression>());
            return Expression.Lambda<Func<T, bool>>(Expression.And(expr1.Body, right), expr1.Parameters);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <returns></returns>
        public static Expression<Func<T, bool>> False<T>()
        {
            return Expression.Lambda<Func<T, bool>>(Expression.Constant(false, typeof(bool)), new ParameterExpression[] { Expression.Parameter(typeof(T), "f") });
        }

        /// <summary>
        /// 
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="expr1"></param>
        /// <param name="expr2"></param>
        /// <returns></returns>
        public static Expression<Func<T, bool>> Or<T>(this Expression<Func<T, bool>> expr1, Expression<Func<T, bool>> expr2)
        {
            InvocationExpression right = Expression.Invoke(expr2, expr1.Parameters.Cast<Expression>());
            return Expression.Lambda<Func<T, bool>>(Expression.Or(expr1.Body, right), expr1.Parameters);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <returns></returns>
        public static Expression<Func<T, bool>> True<T>()
        {
            return Expression.Lambda<Func<T, bool>>(Expression.Constant(true, typeof(bool)), new ParameterExpression[] { Expression.Parameter(typeof(T), "f") });
        }

        /// <summary>
        /// 
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="expression"></param>
        /// <param name="orderBy"></param>
        /// <returns></returns>
        public static Expression<Func<T, bool>> OrderBy<T>(this Expression<Func<T, bool>> expression, Expression<Func<T, bool>> orderBy)
        {
            MethodCallExpression orderByExpression = Expression.Call(orderBy, "OrderBy", new Type[] { typeof(T) }, orderBy.Parameters.Cast<Expression>().ToArray());

            return Expression.Lambda<Func<T, bool>>(orderByExpression, expression.Parameters);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="first"></param>
        /// <param name="second"></param>
        /// <returns></returns>
        public static LambdaExpression And(this Expression first, LambdaExpression second)
        {
            InvocationExpression right = Expression.Invoke(first, second.Parameters);
            return Expression.Lambda(Expression.And(second.Body, right), second.Parameters);
        }
    }
}
